package server;

import java.io.*;
import java.sql.*;
import java.util.ArrayList;
import java.util.Stack;

import javax.xml.soap.Node;

import protocol.AttachingNodeException;
import protocol.ProtocolServer;
import protocol.RemovingNodeException;
import protocol.SchemaException;
import server.dbInfo.TableInfo;
import server.tasks.TransactionFixerTask;


public class NodeHandler {
	final public static char commentChar = '#';
	private static char valueSep = ProtocolServer.VALUE_SEP;
	
	public static void addNode(NodeInfo newNode, ArrayList<NodeInfo> nodes) throws AttachingNodeException {
		if (nodes.size() > 0) { // jesli dodajemy pierwszy wezel to nie sprawdzamy jego schematu
								// zawsze przeladowujemy schemat przy dodawaniu wezla bo nie kontrolujemy DML-i			
			ArrayList<TableInfo> currentInfo = null, newNodeInfo = null;

			try {
				currentInfo = TableInfo.load(nodes.get(0)); 
				newNodeInfo = TableInfo.load(newNode);
			} catch (SQLException e) {
				throw new AttachingNodeException(newNode, e.getMessage());
			}

			// do poprawy
			if (currentInfo.size() != newNodeInfo.size()) // jesli jest rozna liczba tabel to nie zaglebiaj sie w szczegoly
				throw new SchemaException(newNode, "Nowy wezel posiada inna liczba tabel niz klaster");

			for (int i = 0; i < currentInfo.size(); i++) {
				// informacje o tabelach sa ladowane posortowane wiec mozna porownywac jedna po drugiej
				if (!currentInfo.get(i).equals(newNodeInfo.get(i)))
					throw new SchemaException(newNode, 
											"Tabela z nowego wezla " 
											+ newNodeInfo.get(i).tableName 
											+ " nie istnieje w klastrze");
			}
		}
		//else canConnect

		for (NodeInfo node : nodes) {
			if (node.getNodeName().equals(newNode.getNodeName())) {
				throw new AttachingNodeException(newNode, "Wezel z taka sama nazwa juz istnieje");
			}
		}
			
		newNode.setConnected(true);
		newNode.setAttached(true);
		nodes.add(newNode);		
	}

	public static NodeInfo removeNode(String name, ArrayList<NodeInfo> nodes) throws RemovingNodeException {
		for (NodeInfo node : nodes) {
			if (node.getNodeName().equals(name)) {
				nodes.remove(node);
				return node; 
			}
		}
		throw new RemovingNodeException("Wezel o podanej nazwie nie istnieje");
	}
	
	//true -- udalo sie ponownie podlaczyc wezel i wykonac wszystkie zalegle operacje nonquery
	public static boolean reattachNode(NodeInfo toReattach) {
		Stack<String> sqls = toReattach.getMissedNonQueries();
		if(sqls.empty())
			return true;
		
		Connection conn = null;		
		boolean batchFail = false;
		try {			
			conn = DriverManager.getConnection(toReattach.getUrl(),toReattach.getUser(),toReattach.getPassword());

			conn.setAutoCommit(false);
			Statement stmt = conn.createStatement();
			for(String nonquery : sqls) {
				stmt.addBatch(nonquery);
			}
			
			int [] batchResult = stmt.executeBatch();
			
			for(int i : batchResult)  {
				if(i == Statement.EXECUTE_FAILED) {
					batchFail = true;
					break;
				}
			}
			
			if(!batchFail) {
				conn.commit();
				toReattach.getMissedNonQueries().clear();
			}
			
			conn.close();
		} catch (SQLException e1) {
			Logger.logEvent("Aktualizacja zaleglych zdan nonquery." +
							"nie powiodla sie na wezle" +
							toReattach.getNodeName() + " (" +
							toReattach.getIP() + 
							") ktory jest ponownie przylaczany po awarii.", Logger.LOG_ERROR);
			
			if(conn != null)
				try {
					conn.close();
				} catch (SQLException e2) {}
				
			return false;
		}

		return !batchFail;
	}
	
	public static ArrayList<NodeInfo> loadNodes(String path) throws IOException, AttachingNodeException {
		ArrayList<NodeInfo> nodes = new ArrayList<NodeInfo>();
		BufferedReader reader = new BufferedReader(new FileReader(path));
		String line = reader.readLine();
		NodeInfo newNode = null;
		
		while (line != null) {			
			String[] tokens = line.split("\\$");
	
			if (tokens[0].charAt(0) != commentChar 
				&& (tokens.length == 6 || tokens.length == 8)) {		
				newNode = new NodeInfo(tokens);
				addNode(newNode, nodes);
			} 
	
			line = reader.readLine();
		}
		reader.close();
		return nodes;
	}	
	
	public static String saveNodes(Iterable<NodeInfo> nodesToSave){
		StringBuilder result = new StringBuilder(64);
				
		for(NodeInfo node : nodesToSave) {
		  result.append(node.getNodeName()).append(valueSep)
				.append(node.getIP()).append(valueSep)
		  		.append(node.getPort()).append(valueSep)
				.append(node.getDbName()).append(valueSep)
				.append(node.getUser()).append(valueSep)
				.append(node.getPassword());
					  
				if(node.isUpdateFailed())
				{
					result.append(valueSep);
					
					if(node.getFailedOperation() == UpdateFailureType.Commit)
						result.append("COMMIT");
					else
						result.append("ROLLBACK");
					
					result.append(valueSep)
						  .append(node.getFailedXid().toString());
				}
				
				result.append('\n');
		}
		
		return result.toString();
	}
}


